﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Jewelnoid
{
    class Level : SceneBase
    {
        public Vector2 windowSize;
        public Background background;
        public Pallet pallet;
        public Interface lvlInterface;
        public bool ballReleased;
        public bool ballCollidedBlock;
        public bool ballUnstoppable;

        public bool pause;

        public BallManager ballManager;
        public BlockManager blockManager;
        public SpecialPowerManager specialPowerManager;
        public FloatingScoreManager floatingScoreManager;
        public ParticleManager particleManager;

        public InputController input;

        private List<Ball> ballsToRemoveList;
        private List<UnbreakableBlock> unbrekeableBlocksToRemoveList;
        private int extraBallsNumber;
        private double time;

        public override void start()
        {
            this.windowSize = new Vector2(Game1.self.graphics.PreferredBackBufferWidth, Game1.self.graphics.PreferredBackBufferHeight);
            SpriteFont spritefont = Game1.self.Content.Load<SpriteFont>("Arial");
            SpriteFont spritefontBitmap = Game1.self.Content.Load<SpriteFont>("Visitor");

            Texture2D palletTexture = Game1.self.Content.Load<Texture2D>("Pallet");
            Texture2D ballTexture = Game1.self.Content.Load<Texture2D>("Ball");
            Texture2D blockTexture = Game1.self.Content.Load<Texture2D>("Block");
            Texture2D specialPowersTexture = Game1.self.Content.Load<Texture2D>("SpecialPowers");

            this.background = new Background(Game1.self.Content.Load<Texture2D>("Background"));
            this.pallet = new Pallet(palletTexture, new Vector2(this.windowSize.X / 2, this.windowSize.Y - (3 * palletTexture.Height)), 3);
            this.ballManager = new BallManager(ballTexture, new Vector2(this.windowSize.X / 2, this.windowSize.Y - (3 * palletTexture.Height) - ballTexture.Height), 5);
            this.blockManager = new BlockManager(blockTexture, this.windowSize);
            this.lvlInterface = new Interface(spritefont, palletTexture, ballTexture, this.windowSize);
            this.specialPowerManager = new SpecialPowerManager(specialPowersTexture);
            this.floatingScoreManager = new FloatingScoreManager(spritefontBitmap);
            this.particleManager = new ParticleManager(Game1.self.Content.Load<Texture2D>("Particle"));

            this.input = new InputController();
            this.ballsToRemoveList = new List<Ball>();
            this.unbrekeableBlocksToRemoveList = new List<UnbreakableBlock>();

            this.ballUnstoppable = false;
            this.ballReleased = false;
            this.ballCollidedBlock = false;
            this.extraBallsNumber = 2;
            this.time = 0;

            this.pause = false;
        }

        public override void terminate()
        {
            background = null;
            pallet = null;
            ballManager = null;
            blockManager = null;
            lvlInterface = null;
            specialPowerManager = null;
            floatingScoreManager = null;
            particleManager = null;
            ballsToRemoveList = null;
            unbrekeableBlocksToRemoveList = null;
        }

        public override void draw(SpriteBatch spriteBatch)
        {
            background.draw(spriteBatch);
            pallet.draw(spriteBatch);
            ballManager.draw(spriteBatch);
            blockManager.draw(spriteBatch);
            lvlInterface.draw(spriteBatch);
            specialPowerManager.draw(spriteBatch);
            floatingScoreManager.draw(spriteBatch);
            particleManager.draw(spriteBatch);
        }

        public static Color genericColor(int seed)
        {
            Random rad = new Random();
            Random random = new Random(seed * rad.Next());
            int number = random.Next(100);

            if (number < 14)
            {
                return Color.Blue;
            }
            else if (number < 28)
            {
                return Color.Yellow;
            }
            else if (number < 43)
            {
                return Color.Red;
            }
            else if (number < 57)
            {
                return Color.Green;
            }
            else if (number < 71)
            {
                return Color.Purple;
            }
            else if (number < 86)
            {
                return Color.Orange;
            }

            return Color.White;
        }

        public override void update(GameTime time)
        {
            if (!pause)
            {
                if (this.input.isKeyJustPressed(Keys.P))
                {
                    this.pause = true;
                }
                else
                {
                    if (this.input.isKeyJustPressed(Keys.P))
                    {
                        this.pause = false;
                    }
                }

                this.input.getState();

                this.ballManager.update();
                this.pallet.updateRect();
                this.blockManager.update();
                this.lvlInterface.update();
                this.floatingScoreManager.update();
                this.particleManager.update();
                this.specialPowerManager.update(time);
                this.specialPowerManager.removePowersFromList(this.windowSize.Y);

                SpecialPower power = this.specialPowerManager.checkSpecialPowerCollision(this.pallet);

                this.ballCollidedBlock = false;

                if (power != null)
                {
                    applyPower(power, time);
                    power = null;
                }

                if (this.input.keyboard.IsKeyDown(Keys.Left) && this.pallet.position.X - (this.pallet.texture.Width * this.pallet.scale.X) / 2 > 0)
                {
                    this.pallet.position.X -= this.pallet.speed;
                }
                else if (this.input.keyboard.IsKeyDown(Keys.Right) && this.pallet.position.X + (this.pallet.texture.Width * this.pallet.scale.X) / 2 < this.windowSize.X)
                {
                    this.pallet.position.X += this.pallet.speed;
                }
                else if (this.input.isMouseLeftButtonPressed() && this.input.mouse.X - (this.pallet.texture.Width * this.pallet.scale.X) / 2 > 0 && this.input.mouse.X + (this.pallet.texture.Width * this.pallet.scale.X) / 2 < this.windowSize.X)
                {
                    this.pallet.position.X = this.input.mouse.X;
                }

                if (!this.ballReleased)
                {
                    this.ballManager.ballsList[0].position.X = this.pallet.position.X;
                }
                else
                {
                    foreach (Ball ball in this.ballManager.ballsList)
                    {
                        for (int i = 0; i > -1; i++) //Aqui eu uso o conceito de andar de 1 em 1 até a velocidade estipulada para testar a todo momento se houve colisão
                        {
                            if (i < Math.Abs(ball.speed.X))
                            {
                                if (ball.speed.X < 0)
                                {
                                    ball.position.X--;
                                }
                                else
                                {
                                    ball.position.X++;
                                }
                            }

                            if (i < Math.Abs(ball.speed.Y))
                            {
                                if (ball.speed.Y < 0)
                                {
                                    ball.position.Y--;
                                }
                                else
                                {
                                    ball.position.Y++;
                                }
                            }

                            ball.updateRect();

                            if (i >= Math.Abs(ball.speed.X) && i >= Math.Abs(ball.speed.Y))
                            {
                                break;
                            }

                            setBallSpeedAfterCollision(this.pallet, ball);

                            foreach (UnbreakableBlock block in this.blockManager.unbreakableBlocksList)
                            {
                                Vector2 speedOri = ball.speed;

                                if (setBallSpeedAfterCollision(block, ball))
                                {
                                    if (this.ballUnstoppable)
                                    {
                                        unbrekeableBlocksToRemoveList.Add(block);
                                    }

                                    if (speedOri.X != ball.speed.X)
                                    {
                                        if (ball.speed.X > 0)   // Colisão na lateral esquerda
                                        {
                                            this.particleManager.createParticlesEffect(new Vector2(block.position.X + block.texture.Width / 2, block.position.Y), new Vector2(1, 0));
                                        }
                                        else                    // Colisão com lateral direita
                                        {
                                            this.particleManager.createParticlesEffect(new Vector2(block.position.X - block.texture.Width / 2, block.position.Y), new Vector2(-1, 0));
                                        }
                                    }
                                    else
                                    {
                                        if (ball.speed.Y > 0)   // Colisão com parte de baixo
                                        {
                                            this.particleManager.createParticlesEffect(new Vector2(block.position.X, block.position.Y + block.texture.Width / 2), new Vector2(0, 1));
                                        }
                                        else                    // Colisão com parte de cima
                                        {
                                            this.particleManager.createParticlesEffect(new Vector2(block.position.X, block.position.Y - block.texture.Width / 2), new Vector2(0, -1));
                                        }
                                    }

                                    break;
                                }
                            }

                            foreach (Block block in this.blockManager.blocksList)
                            {
                                if (ball.checkCollision(block))
                                {
                                    if (!this.ballUnstoppable)
                                    {
                                        setBallSpeedAfterCollision(block, ball);
                                    }

                                    int blocksRemoved = this.blockManager.removeBlock(block);
                                    int scoreNumber = blocksRemoved * block.getPoints();

                                    this.specialPowerManager.createPower(block.position, 9); //Para testar Power Ups, mudar de blocksRemoved para 9 e sempre gera power Up

                                    this.lvlInterface.score.scoreNumber += scoreNumber;

                                    this.floatingScoreManager.create(FloatingScore.getFloatingText(scoreNumber, blocksRemoved), block.position);

                                    this.blockManager.update();

                                    this.ballCollidedBlock = true;
                                    break;
                                }
                            }
                        }

                        applyWindowCollision(ball);
                    }

                    foreach (Ball ball in this.ballsToRemoveList)
                    {
                        this.ballManager.ballsList.Remove(ball);
                    }

                    foreach (UnbreakableBlock block in this.unbrekeableBlocksToRemoveList)
                    {
                        block.alpha = 1;

                        this.blockManager.unbreakableBlocksList.Remove(block);
                    }
                }

                if (this.ballUnstoppable && this.time < time.TotalGameTime.TotalSeconds - 5)
                {
                    this.ballUnstoppable = false;
                }

                changeBallColorUnstopable();

                if (this.input.isKeyJustPressed(Keys.Space))
                {
                    this.ballReleased = true;
                }
            }
        }

        public void changeBallColorUnstopable()
        {
            foreach (Ball ball in this.ballManager.ballsList)
            {
                if (this.ballUnstoppable)
                {
                    ball.color = Color.Red;
                }
                else
                {
                    ball.color = Color.White;
                }
            }
        }

        /// <summary>
        ///     Testa se a bola bate nas paredes ou sai da tela pela parte de baixo. 
        ///     Caso seja o caso, altera a velocidade da bola (no caso das paredes) e faz os efeitos da bola sair na parte de baixo.
        /// </summary>
        public void applyWindowCollision(Ball ball)
        {
            if ((ball.position.X + ball.texture.Width / 2 > this.windowSize.X) && ball.speed.X > 0) //Estou na direita da tela e não mexi na velocidade ainda
            {
                ball.speed.X *= -1;
                this.particleManager.createParticlesEffect(new Vector2(this.windowSize.X, ball.position.Y), new Vector2(-1, 0));
            }

            if ((ball.position.X - ball.texture.Width / 2 < 0) && ball.speed.X < 0) // Estou na esquerda da tela e não mexi na velocidade ainda
            {
                ball.speed.X *= -1;
                this.particleManager.createParticlesEffect(new Vector2(0, ball.position.Y), new Vector2(1, 0));
            }

            if (ball.position.Y - ball.texture.Height / 2 < 0 && ball.speed.Y < 0) //Estou no teto e não mexi na velocidade ainda
            {
                ball.speed.Y *= -1;
                this.particleManager.createParticlesEffect(ball.position - new Vector2(0, ball.texture.Height / 2), new Vector2(0, 1));
            }

            if (ball.position.Y - ball.texture.Height > this.windowSize.Y)
            {
                if (this.ballManager.ballsList.Count > 1)
                {
                    this.ballsToRemoveList.Add(ball);
                }
                else
                {
                    ball.position = new Vector2(this.pallet.position.X, this.pallet.position.Y - this.pallet.texture.Height);
                    this.ballReleased = false;
                    ball.speed = ball.originalSpeed;
                    this.lvlInterface.lifes--;
                    removePowers();
                }
            }
        }

        public void removePowers()
        {
            this.specialPowerManager.erasePowersList();
            this.ballUnstoppable = false;
            this.pallet.scale = new Vector2(1.0f, 1.0f);
        }

        //Retorna verdadeiro se hovue colisão e falso se não houve, já com a velocidade setada
        public bool setBallSpeedAfterCollision(Object2D obj, Ball ball)
        {
            if (ball.checkCollision(obj))
            {
                //Console.Write("Colidi!\n");
                if (ball.position.Y >= obj.position.Y - obj.texture.Height / 2 &&
                    ball.position.Y <= obj.position.Y + obj.texture.Height / 2) //Colisão com o lado direito ou esquerdo do obj
                {
                    //Console.Write("Colidi com o lado direito ou esquerdo!\n");
                    if (((ball.position.X < obj.position.X && ball.speed.X > 0) && (ball.speed.X > 0)) || //Colisão com lado esquerdo e eu não mexi na velocidade ainda
                        ((ball.position.X > obj.position.X && ball.speed.X < 0) && (ball.speed.X < 0)))  //Colisão com lado direito e eu não mexi na velocidade ainda
                    {
                        ball.speed.X *= -1;
                    }
                }
                else  //Colisão com o lado de cima ou de baixo ou na quina
                {
                    if ((ball.position.Y > obj.position.Y && ball.speed.Y < 0) || //Colisão em baixo
                        (ball.position.Y < obj.position.Y && ball.speed.Y > 0)) //Colisão em cima
                    {
                        if ((ball.position.Y < obj.position.Y && ball.speed.Y > 0) && ball.speed.Y > 0)//Colisão em cima e eu não mexi na velocidade
                        {
                            if (obj.GetType().Name == typeof(Pallet).Name)
                            {
                                ball.calculateSpeed(new Vector2(ball.position.X - obj.position.X, ball.position.Y - obj.position.Y));
                            }
                            else
                            {
                                ball.speed.Y *= -1;
                            }
                        }
                        else if ((ball.position.Y > obj.position.Y && ball.speed.Y < 0) && ball.speed.Y < 0) //Colisão em baixo e eu não mexi na velocidade
                        {
                            ball.speed.Y *= -1;
                        }
                    }
                    else   // Colisão na quina
                    {
                        ball.speed.X *= -1;
                    }
                }

                return true;
            }

            return false;
        }

        public void applyPower(SpecialPower specialPower, GameTime time)
        {
            switch (specialPower.power)
            {
                case SpecialPower.Power.StretchBar:
                    if (this.pallet.maxSize > this.pallet.texture.Width * this.pallet.scale.X)
                    {
                        this.pallet.scale.X = this.pallet.scale.X + 0.4f;
                        this.pallet.rect.Width = (int)(this.pallet.texture.Width * this.pallet.scale.X);
                    }
                    break;

                case SpecialPower.Power.ShrinkBar:
                    if (this.pallet.minSize < this.pallet.texture.Width * this.pallet.scale.X)
                    {
                        this.pallet.scale.X = this.pallet.scale.X - 0.2f;
                        this.pallet.rect.Width = (int)(this.pallet.texture.Width * this.pallet.scale.X);
                    }
                    break;

                case SpecialPower.Power.Multiball:
                    for (int i = 0; i < this.extraBallsNumber; i++)
                    {
                        Ball ball = new Ball(this.ballManager.ballsList[0].texture, this.ballManager.ballsList[0].position, this.ballManager.ballsList[0].speedUnit);

                        ball.setGenericDirection(i);
                        this.ballManager.ballsList.Add(ball);
                    }
                    break;

                case SpecialPower.Power.Unstoppable:
                    foreach (Ball ball in this.ballManager.ballsList)
                    {
                        ball.color = Color.Red;
                        this.ballUnstoppable = true;
                        this.time = time.TotalGameTime.TotalSeconds;
                    }
                    break;

                case SpecialPower.Power.LifeUp:
                    this.lvlInterface.lifes++;
                    break;
            }   
        }
    }
}
